home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir30 / eerems11.zip / EER_CORE.C next >
C/C++ Source or Header  |  1994-10-23  |  47KB  |  1,065 lines

  1. /* ***************************************************************
  2.  
  3. An AutoCAD ADS program to remove "extra entities"
  4.  
  5. This module contains the core function that actually removes
  6. the entities.  Although this is a long and somewhat complex function,
  7. I have left it as one function in order to maintain maximum
  8. execution speed.
  9.  
  10. Jon Fleming    CIS 70334,2443    July 19, 1994
  11.  
  12. Public Domain; Please give me credit if you use this or any
  13. significant portion of it
  14.  
  15. Revision history:
  16.  
  17. July 19, 1994  Version 1.0: Initial release
  18.  
  19. October 23, 1994 Version 1.1: Modified EER_CORE.C to handle negative line
  20.     endpoints properly, and to avoid checking for entities to remove when
  21.     no entities were selected in the "candidates for removal" selection set.
  22.     Modified EER_CORE.C and EEREM.H to include version number report when
  23.     DoEntityRemoval starts.
  24.  
  25. *********************************************************** */
  26.  
  27. #include  <stdio.h>
  28. #include <math.h>
  29. #include <time.h>
  30. #include  "adslib.h"
  31. #include "eerem.h"
  32.  
  33. /* Function prototypes */
  34.  
  35. void DoEntityRemoval(ads_name);
  36.  
  37. extern short ColorStrToNum (char *);
  38. extern ads_real DistancePointToLine(ads_point, ads_point, ads_point);
  39. extern ads_real TransformAngle(ads_real, ads_point, ads_point);
  40. extern int AngleIsBetween (ads_real, ads_real, ads_real);
  41. extern VertexData GetVertexData(ads_name);
  42. extern void SecToHMS (float, long *, long *, float *);
  43.  
  44. /* The constant 2*pi */
  45.  
  46. extern ads_real TwoPi;
  47.  
  48. /* Indicators for whether to consider layer, linetype, and/or color */
  49.  
  50. extern int CheckLayer, CheckLType, CheckColor;
  51.  
  52. /* Tolerance within which numbers are considered equal (at least, that's
  53.    approximately what this is; see below for more detail)  */
  54.  
  55. extern ads_real Tolerance;
  56.  
  57.  
  58. /*  At last!  The function that actually _removes_ extra entities! */
  59.  
  60. void DoEntityRemoval(ads_name EntitiesToCheck) {
  61.  
  62.    char CurrentEntityLayer[32], CurrentEntityLType[32];
  63.    char CandidateLayer[32], CandidateLType[32];
  64.    char CurrentEntityType[16], CandidateEntityType[16];
  65.  
  66.    short CurrentEntityColor, CoordNum;
  67.    short MaxColor, MinColor;
  68.    short CurrentEntityFlag, CurrentEntityFit;
  69.    short CandidateFlag, CandidateFit;
  70.  
  71.    long CurrentEntityNumber, CandidateNumber, PointsRemoved = 0L;
  72.    long LinesRemoved = 0L, ArcsRemoved = 0L, CirclesRemoved = 0L;
  73.    long PlinesRemoved = 0L, TotalRemoved, ReportIncrement;
  74.    long NumEntsToCheck, NumEntsSelected;
  75.  
  76.    ads_name CurrentEntityName, CandidateName, DeletionCandidates;
  77.    ads_name CurrentEntityVertex, CandidateVertex;
  78.  
  79.    ads_point CurrentEntityP1, CurrentEntityP2, CurrentEntityEVec;
  80.    ads_point CandidateP1, CandidateP2, CandidateEVec;
  81.    ads_point BrickCorner1, BrickCorner2, BrickCorner3, BrickCorner4;
  82.    ads_point TempPoint;
  83.  
  84.    ads_real CurrentEntitySize1, CurrentEntitySize2;
  85.    ads_real CurrentEntityStartAng, CurrentEntityEndAng;
  86.    ads_real CandidateStartAng, CandidateEndAng, TempStartAng, TempEndAng;
  87.    ads_real CandidateStartWidth, CandidateEndWidth;
  88.    ads_real TempReal, CurrentDistance;
  89.  
  90.    VertexData CandidateVertexData, CurrentEntityVertexData;
  91.  
  92.    struct resbuf *CurrentEAL, *CurrentEALItem, *CandidateEAL;
  93.    struct resbuf *CandidateEALItem, *SelSetCriteria;
  94.    struct resbuf SysVarResBuf, ECSResBuf1, ECSResBuf2;
  95.  
  96.    clock_t StartTime, EndTime;
  97.  
  98.    float TotalSeconds, ElapsedSeconds, EntitiesPerSecond;
  99.    long ElapsedHours, ElapsedMinutes;
  100.  
  101.    int DecimalPlaces, Synchronized, ReachedLastVertex;
  102.  
  103.    TwoPi = 2.0*acos(-1.0);
  104.  
  105.    StartTime = clock();
  106.  
  107.    ads_printf("\nExtra Entity Removal version %s", EEREM_VER);
  108.  
  109.    /* End any undo group and start a new one */
  110.  
  111.    ads_command(RTSTR, "._UNDO", RTSTR, "_GROUP", RTNONE);
  112.  
  113.    /* Find out how many entities we have to check */
  114.  
  115.    if (ads_sslength(EntitiesToCheck, &NumEntsToCheck) != RTNORM) {
  116.       NumEntsToCheck = 0L;
  117.    }
  118.    ads_printf("\nChecking %lu entities", NumEntsToCheck);
  119.  
  120.    /* Determine an increment at which we will report progress to the user */
  121.  
  122.    if (NumEntsToCheck <= REPORTTHRESHHOLD) {
  123.       ReportIncrement = NumEntsToCheck;
  124.    }
  125.    else if (NumEntsToCheck > 100*REPORTTHRESHHOLD) {
  126.       ReportIncrement = NumEntsToCheck/100;
  127.    }
  128.    else {
  129.       ReportIncrement = REPORTTHRESHHOLD;
  130.    }
  131.  
  132.    ads_printf("\n");
  133.  
  134.    /* Loop over all entities in the selection set */
  135.  
  136.    for (CurrentEntityNumber = 0L; CurrentEntityNumber < NumEntsToCheck; CurrentEntityNumber++) {
  137.  
  138.       /* Check for the user cancelling at a point at which we don't have any
  139.          memory allocated that we have to worry about freeing */
  140.  
  141.       if (ads_usrbrk()) {
  142.          ads_printf("\n**Cancel**\n");
  143.          return;
  144.       }
  145.  
  146.       /* Get the address of the Entity Association List of the current entity */
  147.  
  148.       ads_ssname(EntitiesToCheck, CurrentEntityNumber, CurrentEntityName);
  149.  
  150.       /* Check that we haven't already deleted this entity */
  151.  
  152.       if ((CurrentEAL = CurrentEALItem = ads_entget(CurrentEntityName)) != NULL) {
  153.  
  154.          /* Set up default values for items which may not be in the EAL of the
  155.             current entity */
  156.  
  157.          CurrentEntityColor = 256;
  158.          strcpy(CurrentEntityLType, "BYLAYER");
  159.          CurrentEntityFlag = CurrentEntityFit = 0;
  160.          CurrentEntityEVec[X] = 0.0;
  161.          CurrentEntityEVec[Y] = 0.0;
  162.          CurrentEntityEVec[Z] = 1.0;
  163.          CurrentEntitySize1 = 0.0;
  164.          CurrentEntitySize2 = 0.0;
  165.  
  166.          /* Extract the type, layer, color, linetype and any defining
  167.             points, radius, start and end angle, and extrusion vector of
  168.             the current entity */
  169.  
  170.          while (CurrentEALItem != NULL) {
  171.             switch (CurrentEALItem->restype) {
  172.                case 0: {
  173.                   strcpy(CurrentEntityType, CurrentEALItem->resval.rstring);
  174.                   break;
  175.                }
  176.                case 6: {
  177.                   strcpy(CurrentEntityLType, CurrentEALItem->resval.rstring);
  178.                   break;
  179.                }
  180.                case 8: {
  181.                   strcpy(CurrentEntityLayer, CurrentEALItem->resval.rstring);
  182.                   break;
  183.                }
  184.                case 10: {
  185.                   ads_point_set(CurrentEALItem->resval.rpoint, CurrentEntityP1);
  186.                   break;
  187.                }
  188.                case 11: {
  189.                   ads_point_set(CurrentEALItem->resval.rpoint, CurrentEntityP2);
  190.                   break;
  191.                }
  192.                case 40: {
  193.                   CurrentEntitySize1 = CurrentEALItem->resval.rreal;
  194.                   break;
  195.                }
  196.                case 41: {
  197.                   CurrentEntitySize2 = CurrentEALItem->resval.rreal;
  198.                   break;
  199.                }
  200.                case 50: {
  201.                   CurrentEntityStartAng = CurrentEALItem->resval.rreal;
  202.                   break;
  203.                }
  204.                case 51: {
  205.                   CurrentEntityEndAng = CurrentEALItem->resval.rreal;
  206.                   break;
  207.                }
  208.                case 62: {
  209.                   CurrentEntityColor = CurrentEALItem->resval.rint;
  210.                   break;
  211.                }
  212.                case 70: {
  213.                   CurrentEntityFlag = CurrentEALItem->resval.rint;
  214.                   break;
  215.                }
  216.                case 75: {
  217.                   CurrentEntityFit = CurrentEALItem->resval.rint;
  218.                   break;
  219.                }
  220.                case 210: {
  221.                   ads_point_set(CurrentEALItem->resval.rpoint, CurrentEntityEVec);
  222.                   break;
  223.                }
  224.             }
  225.             CurrentEALItem = CurrentEALItem->rbnext;
  226.          }
  227.  
  228.          /* Release the resbuf of the current entity's Association List */
  229.  
  230.          ads_relrb(CurrentEAL);
  231.  
  232.          /* If we don't care about layer or linetype or color, set the
  233.             layer or linetype to be matched to a match-anything wild card */
  234.  
  235.          if (CheckLayer == FALSE) {
  236.             strcpy(CurrentEntityLayer, "*");
  237.          }
  238.          if (CheckLType == FALSE) {
  239.             strcpy(CurrentEntityLType, "*");
  240.          }
  241.          if (CheckColor == FALSE) {
  242.             MaxColor = 256;
  243.             MinColor = 0;
  244.          }
  245.          else {
  246.             MaxColor = MinColor = CurrentEntityColor;
  247.          }
  248.  
  249.          /* Start looking for deletion candidates; first separate by the
  250.             entity type of the current entity */
  251.  
  252.          /* SECTION TO HANDLE LINES */
  253.  
  254.          if (strcmp(CurrentEntityType, "LINE") == 0) {
  255.  
  256.             /* Skip any lines that are less than 1/1000 of the tolerance
  257.                long, since they may be zero length (which will make the
  258.                point-to-line distance calculation blow up) and we'll
  259.                presume that they will be deleted later when checking some
  260.                other line */
  261.  
  262.             if (ads_distance(CurrentEntityP1, CurrentEntityP2) >= (Tolerance/1000.0)) {
  263.  
  264.                /* Set up two points defining diagonally opposite corners of
  265.                   a brick, P1 being the corner of the brick with the minimum
  266.                   coordinates and P2 being the corner of the brick with the
  267.                   maximum coordinates.  Any lines that may be
  268.                   deleted must lie within this brick. */
  269.  
  270.                ads_point_set(CurrentEntityP1, BrickCorner1);
  271.                ads_point_set(CurrentEntityP2, BrickCorner2);
  272.  
  273.                for (CoordNum = 0; CoordNum <= 2; CoordNum++) {
  274.                   if (BrickCorner1[CoordNum] > BrickCorner2[CoordNum]) {
  275.                      TempReal = BrickCorner1[CoordNum];
  276.                      BrickCorner1[CoordNum] = BrickCorner2[CoordNum];
  277.                      BrickCorner2[CoordNum] = TempReal;
  278.                   }
  279.  
  280.                   /* If any side is smaller than twice Tolerance ... */
  281.  
  282.                   if (fabs((BrickCorner2[CoordNum] - BrickCorner1[CoordNum])) < 2.0*Tolerance) {
  283.  
  284.                      /* Make it equal to twice Tolerance */
  285.  
  286.                      TempReal = fabs(BrickCorner2[CoordNum] + BrickCorner1[CoordNum])/2.0;
  287.                      BrickCorner1[CoordNum] = TempReal - Tolerance;
  288.                      BrickCorner2[CoordNum] = TempReal + Tolerance;
  289.                   }
  290.                   else {
  291.  
  292.                      /* Otherwise, increase the side of the brick by a tiny
  293.                         amount */
  294.  
  295.                      TempReal = fabs(BrickCorner2[CoordNum] + BrickCorner1[CoordNum])*Tolerance*0.01;
  296.                      BrickCorner1[CoordNum] = BrickCorner1[CoordNum] - TempReal;
  297.                      BrickCorner2[CoordNum] = BrickCorner2[CoordNum] + TempReal;
  298.                   }
  299.                }
  300.  
  301.                /* Get a selection set of all lines lying within the brick
  302.                   of which the current LINE entity is a diagonal, with
  303.                   appropriate colors, linetypes, and layers */
  304.  
  305.                SelSetCriteria = ads_buildlist(RTDXF0, "LINE",
  306.                                              -4, ">=,>=,>=",
  307.                                                 10, BrickCorner1,
  308.                                              -4, "<=,<=,<=",
  309.                                                 10, BrickCorner2,
  310.                                              -4, ">=,>=,>=",
  311.                                                 11, BrickCorner1,
  312.                                              -4, "<=,<=,<=",
  313.                                                 11, BrickCorner2,
  314.                                              6, CurrentEntityLType,
  315.                                              8, CurrentEntityLayer,
  316.                                              -4, ">=",
  317.                                                 62, MinColor,
  318.                                              -4, "<=",
  319.                                                 62, MaxColor,
  320.                                              0);
  321.                ads_ssget("X", NULL, NULL, SelSetCriteria, DeletionCandidates);
  322.                ads_relrb(SelSetCriteria);
  323.  
  324.                /* We got at least one entity, the current one.  If there are
  325.                   other entities ... */
  326.  
  327.                if (ads_sslength(DeletionCandidates, &NumEntsSelected) != RTNORM) {
  328.                   NumEntsSelected = 0L;
  329.                }
  330.                if (NumEntsSelected > 1L) {
  331.  
  332.                   /* Remove the current entity from the selection set */
  333.  
  334.                   ads_ssdel(CurrentEntityName, DeletionCandidates);
  335.                   NumEntsSelected = --NumEntsSelected;
  336.  
  337.                   /* Check each remaining entity */
  338.  
  339.                   for (CandidateNumber = 0L; CandidateNumber < NumEntsSelected; CandidateNumber++) {
  340.                      ads_ssname(DeletionCandidates, CandidateNumber, CandidateName);
  341.                      CandidateEAL = CandidateEALItem = ads_entget(CandidateName);
  342.  
  343.                      /* Extract the pertinent information about the line */
  344.  
  345.                      while (CandidateEALItem != NULL) {
  346.                         switch (CandidateEALItem->restype) {
  347.                            case 10: {
  348.                               ads_point_set(CandidateEALItem->resval.rpoint, CandidateP1);
  349.                               break;
  350.                            }
  351.                            case 11: {
  352.                               ads_point_set(CandidateEALItem->resval.rpoint, CandidateP2);
  353.                               break;
  354.                            }
  355.                         }
  356.                         CandidateEALItem = CandidateEALItem->rbnext;
  357.                      }
  358.  
  359.                      ads_relrb(CandidateEAL);
  360.  
  361.                      /* If neither endpoint of the candidate extends beyond
  362.                         the current line and both endpoints are close enough
  363.                         to being on the current line ... */
  364.  
  365.                      if (((CurrentDistance = DistancePointToLine(CurrentEntityP1, CurrentEntityP2, CandidateP1)) >= 0.0)
  366.                            && (Tolerance > CurrentDistance)
  367.                            && ((CurrentDistance = DistancePointToLine(CurrentEntityP1, CurrentEntityP2, CandidateP2)) >= 0.0)
  368.                            && (Tolerance > CurrentDistance)) {
  369.  
  370.                               /* Delete that entity! */
  371.                               ads_entdel(CandidateName);
  372.                               LinesRemoved = ++LinesRemoved;
  373.                      }
  374.                   }
  375.  
  376.                }
  377.                ads_ssfree(DeletionCandidates);
  378.             }
  379.          }
  380.  
  381.          /* SECTION TO HANDLE CIRCLES (AND UNDERLYING ARCS) */
  382.  
  383.          else if (strcmp(CurrentEntityType, "CIRCLE") == 0) {
  384.  
  385.             /* First transform the center of the current circle into
  386.                the ECS of a circle with an extrusion vector pointing
  387.                180 degrees away from the current circle's extrusion vector */
  388.  
  389.             ECSResBuf1.restype = RT3DPOINT;
  390.             ECSResBuf1.resval.rpoint[0] = CurrentEntityEVec[0];
  391.             ECSResBuf1.resval.rpoint[1] = CurrentEntityEVec[1];
  392.             ECSResBuf1.resval.rpoint[2] = CurrentEntityEVec[2];
  393.  
  394.             ECSResBuf2.restype = RT3DPOINT;
  395.             ECSResBuf2.resval.rpoint[0] = -CurrentEntityEVec[0];
  396.             ECSResBuf2.resval.rpoint[1] = -CurrentEntityEVec[1];
  397.             ECSResBuf2.resval.rpoint[2] = -CurrentEntityEVec[2];
  398.  
  399.             ads_trans(CurrentEntityP1, &ECSResBuf1, &ECSResBuf2, 0, TempPoint);
  400.  
  401.             /* Set up two cubes, inside one of which the center of a
  402.                duplicate circle or underlying arc must lie.  We need
  403.                two cubes because circle and arc center coordinates are
  404.                in ECS, and a circle or arc with an extrusion vector 180
  405.                degrees away from the current entity's extrusion vector
  406.                has very different values for coordinates. */
  407.  
  408.             for (CoordNum = 0; CoordNum <= 2; CoordNum++) {
  409.                BrickCorner1[CoordNum] = CurrentEntityP1[CoordNum] + Tolerance;
  410.                BrickCorner2[CoordNum] = CurrentEntityP1[CoordNum] - Tolerance;
  411.                BrickCorner3[CoordNum] = TempPoint[CoordNum] + Tolerance;
  412.                BrickCorner4[CoordNum] = TempPoint[CoordNum] - Tolerance;
  413.                if (BrickCorner1[CoordNum] > BrickCorner2[CoordNum]) {
  414.                   TempReal = BrickCorner1[CoordNum];
  415.                   BrickCorner1[CoordNum] = BrickCorner2[CoordNum];
  416.                   BrickCorner2[CoordNum] = TempReal;
  417.                }
  418.                if (BrickCorner3[CoordNum] > BrickCorner4[CoordNum]) {
  419.                   TempReal = BrickCorner3[CoordNum];
  420.                   BrickCorner3[CoordNum] = BrickCorner4[CoordNum];
  421.                   BrickCorner4[CoordNum] = TempReal;
  422.                }
  423.             }
  424.  
  425.             /* Get a selection set of all circles or arcs with centers
  426.                inside either cube, radius within Tolerance of the current
  427.                circle's radius, and with appropriate colors, linetypes,
  428.                and layers. */
  429.  
  430.             SelSetCriteria = ads_buildlist(-4, "<OR",
  431.                                              RTDXF0, "CIRCLE",
  432.                                              RTDXF0, "ARC",
  433.                                            -4, "OR>",
  434.                                            -4, "<OR",
  435.                                              -4, "<AND",
  436.                                                 -4, ">=,>=,>=",
  437.                                                    10, BrickCorner1,
  438.                                                 -4, "<=,<=,<=",
  439.                                                    10, BrickCorner2,
  440.                                              -4, "AND>",
  441.                                              -4, "<AND",
  442.                                                 -4, ">=,>=,>=",
  443.                                                    10, BrickCorner3,
  444.                                                 -4, "<=,<=,<=",
  445.                                                    10, BrickCorner4,
  446.                                              -4, "AND>",
  447.                                            -4, "OR>",
  448.                                            -4, ">=",
  449.                                              40, CurrentEntitySize1 - Tolerance,
  450.                                            -4, "<=",
  451.                                              40, CurrentEntitySize1 + Tolerance,
  452.                                            6, CurrentEntityLType,
  453.                                            8, CurrentEntityLayer,
  454.                                            -4, ">=",
  455.                                              62, MinColor,
  456.                                            -4, "<=",
  457.                                              62, MaxColor,
  458.                                           0);
  459.             ads_ssget("X", NULL, NULL, SelSetCriteria, DeletionCandidates);
  460.             ads_relrb(SelSetCriteria);
  461.  
  462.             /* We got at least one entity, the current one.  If there are
  463.                other entities ... */
  464.  
  465.             if (ads_sslength(DeletionCandidates, &NumEntsSelected) != RTNORM) {
  466.                NumEntsSelected = 0L;
  467.             }
  468.             if (NumEntsSelected > 1L) {
  469.  
  470.                /* Remove the current entity from the selection set */
  471.  
  472.                ads_ssdel(CurrentEntityName, DeletionCandidates);
  473.                NumEntsSelected = --NumEntsSelected;
  474.  
  475.                /* Check each remaining entity */
  476.  
  477.                for (CandidateNumber = 0L; CandidateNumber < NumEntsSelected; CandidateNumber++) {
  478.                   ads_ssname(DeletionCandidates, CandidateNumber, CandidateName);
  479.                   CandidateEAL = CandidateEALItem = ads_entget(CandidateName);
  480.  
  481.                   /* We know _almost_ enough to delete the candidate entity;
  482.                      but we're not quite sure the extrusion vectors are
  483.                      parallel or 180 degrees apart */
  484.  
  485.                   /* Extract the extrusion vector and entity type of the
  486.                      candidate */
  487.  
  488.                   while (CandidateEALItem != NULL) {
  489.                      switch (CandidateEALItem->restype) {
  490.                         case 0: {
  491.                            strcpy(CandidateEntityType, CandidateEALItem->resval.rstring);
  492.                         }
  493.                         case 210: {
  494.                            ads_point_set(CandidateEALItem->resval.rpoint, CandidateEVec);
  495.                            break;
  496.                         }
  497.                      }
  498.                      CandidateEALItem = CandidateEALItem->rbnext;
  499.                   }
  500.  
  501.                   ads_relrb(CandidateEAL);
  502.  
  503.                   /* Do the cross product of the two extrusion vectors */
  504.  
  505.                   TempPoint[0] = CurrentEntityEVec[1]*CandidateEVec[2]
  506.                                  - CurrentEntityEVec[2]*CandidateEVec[1];
  507.                   TempPoint[1] = CurrentEntityEVec[2]*CandidateEVec[0]
  508.                                  - CurrentEntityEVec[0]*CandidateEVec[2];
  509.                   TempPoint[2] = CurrentEntityEVec[0]*CandidateEVec[1]
  510.                                  - CurrentEntityEVec[1]*CandidateEVec[0];
  511.  
  512.                   /* Get the magnitude of the cross product, which (for two
  513.                      unit vectors like extrusion vectors) is the sine of
  514.                      the angle between them */
  515.  
  516.                   TempReal = sqrt(TempPoint[0]*TempPoint[0] +
  517.                                     TempPoint[1]*TempPoint[1] +
  518.                                     TempPoint[2]*TempPoint[2]);
  519.  
  520.                   if (TempReal <= Tolerance) {
  521.  
  522.                      /* Delete that entity! */
  523.                      ads_entdel(CandidateName);
  524.  
  525.                      if (strcmp(CandidateEntityType, "CIRCLE") == 0) {
  526.                         CirclesRemoved = ++CirclesRemoved;
  527.                      }
  528.                      else {
  529.                         ArcsRemoved = ++ArcsRemoved;
  530.                      }
  531.                   }
  532.                }
  533.             }
  534.             ads_ssfree(DeletionCandidates);
  535.          }
  536.  
  537.          /* SECTION TO HANDLE POINTS */
  538.  
  539.          else if (strcmp(CurrentEntityType, "POINT") == 0) {
  540.  
  541.             /* Set up a cube inside which any points to be deleted must lie */
  542.  
  543.             for (CoordNum = 0; CoordNum <= 2; CoordNum++) {
  544.                BrickCorner1[CoordNum] = CurrentEntityP1[CoordNum] + Tolerance;
  545.                BrickCorner2[CoordNum] = CurrentEntityP1[CoordNum] - Tolerance;
  546.                if (BrickCorner1[CoordNum] > BrickCorner2[CoordNum]) {
  547.                   TempReal = BrickCorner1[CoordNum];
  548.                   BrickCorner1[CoordNum] = BrickCorner2[CoordNum];
  549.                   BrickCorner2[CoordNum] = TempReal;
  550.                }
  551.             }
  552.  
  553.             /* Get a selection set of all points within the cube with
  554.                appropriate color, linetype, and layer */
  555.  
  556.             SelSetCriteria = ads_buildlist(RTDXF0, "POINT",
  557.                                            -4, ">=,>=,>=",
  558.                                              10, BrickCorner1,
  559.                                            -4, "<=,<=,<=",
  560.                                              10, BrickCorner2,
  561.                                            6, CurrentEntityLType,
  562.                                            8, CurrentEntityLayer,
  563.                                            -4, ">=",
  564.                                              62, MinColor,
  565.                                            -4, "<=",
  566.                                              62, MaxColor,
  567.                                            0);
  568.             ads_ssget("X", NULL, NULL, SelSetCriteria, DeletionCandidates);
  569.             ads_relrb(SelSetCriteria);
  570.  
  571.             /* We got at least one entity, the current one.  If there are
  572.                other entities ... */
  573.  
  574.             if (ads_sslength(DeletionCandidates, &NumEntsSelected) != RTNORM) {
  575.                NumEntsSelected = 0L;
  576.             }
  577.             if (NumEntsSelected > 1L) {
  578.  
  579.                /* Remove the current entity from the selection set */
  580.  
  581.                ads_ssdel(CurrentEntityName, DeletionCandidates);
  582.                NumEntsSelected = --NumEntsSelected;
  583.  
  584.                /* Delete all the points */
  585.  
  586.                for (CandidateNumber = 0L; CandidateNumber < NumEntsSelected; CandidateNumber++) {
  587.                   ads_ssname(DeletionCandidates, CandidateNumber, CandidateName);
  588.                   ads_entdel(CandidateName);
  589.                }
  590.  
  591.                PointsRemoved = PointsRemoved + NumEntsSelected;
  592.             }
  593.             ads_ssfree(DeletionCandidates);
  594.          }
  595.          
  596.          /* SECTION TO HANDLE ARCS */
  597.  
  598.          else if (strcmp(CurrentEntityType, "ARC") == 0) {
  599.  
  600.             /* First transform the center of the current arc into
  601.                the ECS of an arc with an extrusion vector pointing
  602.                180 degrees away from the current arc's extrusion vector */
  603.  
  604.             ECSResBuf1.restype = RT3DPOINT;
  605.             ECSResBuf1.resval.rpoint[0] = CurrentEntityEVec[0];
  606.             ECSResBuf1.resval.rpoint[1] = CurrentEntityEVec[1];
  607.             ECSResBuf1.resval.rpoint[2] = CurrentEntityEVec[2];
  608.  
  609.             ECSResBuf2.restype = RT3DPOINT;
  610.             ECSResBuf2.resval.rpoint[0] = -CurrentEntityEVec[0];
  611.             ECSResBuf2.resval.rpoint[1] = -CurrentEntityEVec[1];
  612.             ECSResBuf2.resval.rpoint[2] = -CurrentEntityEVec[2];
  613.  
  614.             ads_trans(CurrentEntityP1, &ECSResBuf1, &ECSResBuf2, 0, TempPoint);
  615.  
  616.             /* Set up two cubes, inside one of which the center of a
  617.                duplicate or underlying arc must lie.  We need
  618.                two cubes because arc center coordinates are
  619.                in ECS, and an arc with an extrusion vector 180
  620.                degrees away from the current entity's extrusion vector
  621.                has very different values for coordinates. */
  622.  
  623.             for (CoordNum = 0; CoordNum <= 2; CoordNum++) {
  624.                BrickCorner1[CoordNum] = CurrentEntityP1[CoordNum] + Tolerance;
  625.                BrickCorner2[CoordNum] = CurrentEntityP1[CoordNum] - Tolerance;
  626.                BrickCorner3[CoordNum] = TempPoint[CoordNum] + Tolerance;
  627.                BrickCorner4[CoordNum] = TempPoint[CoordNum] - Tolerance;
  628.                if (BrickCorner1[CoordNum] > BrickCorner2[CoordNum]) {
  629.                   TempReal = BrickCorner1[CoordNum];
  630.                   BrickCorner1[CoordNum] = BrickCorner2[CoordNum];
  631.                   BrickCorner2[CoordNum] = TempReal;
  632.                }
  633.                if (BrickCorner3[CoordNum] > BrickCorner4[CoordNum]) {
  634.                   TempReal = BrickCorner3[CoordNum];
  635.                   BrickCorner3[CoordNum] = BrickCorner4[CoordNum];
  636.                   BrickCorner4[CoordNum] = TempReal;
  637.                }
  638.             }
  639.  
  640.             /* Get a selection set of all arcs with centers
  641.                inside either cube, radius within Tolerance of the current
  642.                arc's radius, and with appropriate colors, linetypes,
  643.                and layers. */
  644.  
  645.             SelSetCriteria = ads_buildlist(RTDXF0, "ARC",
  646.                                            -4, "<OR",
  647.                                              -4, "<AND",
  648.                                                 -4, ">=,>=,>=",
  649.                                                    10, BrickCorner1,
  650.                                                 -4, "<=,<=,<=",
  651.                                                    10, BrickCorner2,
  652.                                              -4, "AND>",
  653.                                              -4, "<AND",
  654.                                                 -4, ">=,>=,>=",
  655.                                                    10, BrickCorner3,
  656.                                                 -4, "<=,<=,<=",
  657.                                                    10, BrickCorner4,
  658.                                              -4, "AND>",
  659.                                            -4, "OR>",
  660.                                            -4, ">=",
  661.                                              40, CurrentEntitySize1 - Tolerance,
  662.                                            -4, "<=",
  663.                                              40, CurrentEntitySize1 + Tolerance,
  664.                                            6, CurrentEntityLType,
  665.                                            8, CurrentEntityLayer,
  666.                                            -4, ">=",
  667.                                              62, MinColor,
  668.                                            -4, "<=",
  669.                                              62, MaxColor,
  670.                                           0);
  671.             ads_ssget("X", NULL, NULL, SelSetCriteria, DeletionCandidates);
  672.             ads_relrb(SelSetCriteria);
  673.  
  674.             /* We got at least one entity, the current one.  If there are
  675.                other entities ... */
  676.  
  677.             if (ads_sslength(DeletionCandidates, &NumEntsSelected) != RTNORM) {
  678.                NumEntsSelected = 0L;
  679.             }
  680.             if (NumEntsSelected > 1L) {
  681.  
  682.                /* Remove the current entity from the selection set */
  683.  
  684.                ads_ssdel(CurrentEntityName, DeletionCandidates);
  685.                NumEntsSelected = --NumEntsSelected;
  686.  
  687.                /* Check each remaining entity */
  688.  
  689.                for (CandidateNumber = 0L; CandidateNumber < NumEntsSelected; CandidateNumber++) {
  690.                   ads_ssname(DeletionCandidates, CandidateNumber, CandidateName);
  691.                   CandidateEAL = CandidateEALItem = ads_entget(CandidateName);
  692.  
  693.                   /* Extract the extrusion vector and start and end angles
  694.                      of the candidate */
  695.  
  696.                   while (CandidateEALItem != NULL) {
  697.                      switch (CandidateEALItem->restype) {
  698.                         case 50: {
  699.                            CandidateStartAng = CandidateEALItem->resval.rreal;
  700.                            break;
  701.                         }
  702.                         case 51: {
  703.                            CandidateEndAng = CandidateEALItem->resval.rreal;
  704.                            break;
  705.                         }
  706.                         case 210: {
  707.                            ads_point_set(CandidateEALItem->resval.rpoint, CandidateEVec);
  708.                            break;
  709.                         }
  710.                      }
  711.                      CandidateEALItem = CandidateEALItem->rbnext;
  712.                   }
  713.  
  714.                   ads_relrb(CandidateEAL);
  715.  
  716.                   /* Do the cross product of the two extrusion vectors */
  717.  
  718.                   TempPoint[0] = CurrentEntityEVec[1]*CandidateEVec[2]
  719.                                  - CurrentEntityEVec[2]*CandidateEVec[1];
  720.                   TempPoint[1] = CurrentEntityEVec[2]*CandidateEVec[0]
  721.                                  - CurrentEntityEVec[0]*CandidateEVec[2];
  722.                   TempPoint[2] = CurrentEntityEVec[0]*CandidateEVec[1]
  723.                                  - CurrentEntityEVec[1]*CandidateEVec[0];
  724.  
  725.                   /* Get the magnitude of the cross product, which (for two
  726.                      unit vectors like extrusion vectors) is the sine of
  727.                      the angle between them */
  728.  
  729.                   TempReal = sqrt(TempPoint[0]*TempPoint[0] +
  730.                                     TempPoint[1]*TempPoint[1] +
  731.                                     TempPoint[2]*TempPoint[2]);
  732.  
  733.                   /* Check to make sure the extrusion vectors are
  734.                      parallel or 180 degrees apart */
  735.  
  736.                   if (TempReal <= Tolerance) {
  737.  
  738.                      /* Get the start and end angles of the candidate in
  739.                         the coordinate system of the current entity */
  740.  
  741.                      TempStartAng = TransformAngle(CandidateStartAng,
  742.                                                    CandidateEVec,
  743.                                                    CurrentEntityEVec);
  744.                      TempEndAng = TransformAngle(CandidateEndAng,
  745.                                                  CandidateEVec,
  746.                                                  CurrentEntityEVec);
  747.  
  748.                      /* If both the start and end angles of the candidate
  749.                         entity are between the start and end angles of the
  750.                         current entity ... */
  751.  
  752.                      if ((AngleIsBetween(TempStartAng, CurrentEntityStartAng, CurrentEntityEndAng))
  753.                            && (AngleIsBetween(TempEndAng, CurrentEntityStartAng, CurrentEntityEndAng))) {
  754.  
  755.                         /* Delete that entity! */
  756.                         ads_entdel(CandidateName);
  757.  
  758.                         ArcsRemoved = ++ArcsRemoved;
  759.                      }
  760.                   }
  761.                }
  762.             }
  763.             ads_ssfree(DeletionCandidates);
  764.          }
  765.  
  766.          /* SECTION TO HANDLE NON-MESH POLYLINES */
  767.  
  768.          else if ((strcmp(CurrentEntityType, "POLYLINE") == 0)
  769.                      && (!(16 & CurrentEntityFlag))) {
  770.  
  771.             /* Set up a range of elevations within which any duplicate
  772.                polyline must lie */
  773.  
  774.             BrickCorner1[0] = BrickCorner2[0] = BrickCorner1[1] = BrickCorner2[1] = 0.0;
  775.             BrickCorner1[2] = CurrentEntityP1[2] + Tolerance;
  776.             BrickCorner2[2] = CurrentEntityP1[2] - Tolerance;
  777.             if (BrickCorner1[2] > BrickCorner2[2]) {
  778.                TempReal = BrickCorner1[2];
  779.                BrickCorner1[2] = BrickCorner2[2];
  780.                BrickCorner2[2] = TempReal;
  781.             }
  782.  
  783.             /* Get a selection set of all polylines within that range of
  784.                elevations with appropriate linetype, layer, and color */
  785.  
  786.             SelSetCriteria = ads_buildlist(RTDXF0, "POLYLINE",
  787.                                           -4, ">=,>=,>=",
  788.                                              10, BrickCorner1,
  789.                                           -4, "<=,<=,<=",
  790.                                              10, BrickCorner2,
  791.                                            6, CurrentEntityLType,
  792.                                            8, CurrentEntityLayer,
  793.                                            -4, ">=",
  794.                                              62, MinColor,
  795.                                            -4, "<=",
  796.                                              62, MaxColor,
  797.                                           0);
  798.             ads_ssget("X", NULL, NULL, SelSetCriteria, DeletionCandidates);
  799.             ads_relrb(SelSetCriteria);
  800.  
  801.             /* We got at least one entity, the current one.  If there are
  802.                other entities ... */
  803.  
  804.             if (ads_sslength(DeletionCandidates, &NumEntsSelected) != RTNORM) {
  805.                NumEntsSelected = 0L;
  806.             }
  807.             if (NumEntsSelected > 1L) {
  808.  
  809.                /* Remove the current entity from the selection set */
  810.  
  811.                ads_ssdel(CurrentEntityName, DeletionCandidates);
  812.                NumEntsSelected = --NumEntsSelected;
  813.  
  814.                /* Check each remaining entity */
  815.  
  816.                for (CandidateNumber = 0L; CandidateNumber < NumEntsSelected; CandidateNumber++) {
  817.                   ads_ssname(DeletionCandidates, CandidateNumber, CandidateName);
  818.                   CandidateEAL = CandidateEALItem = ads_entget(CandidateName);
  819.  
  820.                   /* Set up defaults for the polyline type flag and fit */
  821.  
  822.                   CandidateFlag = CandidateFit = 0;
  823.                   CandidateEVec[X] = 0.0;
  824.                   CandidateEVec[Y] = 0.0;
  825.                   CandidateEVec[Z] = 1.0;
  826.  
  827.                   /* Extract the start and end widths, polyline type flag,
  828.                      curve type of the candidate, and extrusion vector of
  829.                      the candidate */
  830.  
  831.                   while (CandidateEALItem != NULL) {
  832.                      switch (CandidateEALItem->restype) {
  833.                         case 40: {
  834.                            CandidateStartWidth = CandidateEALItem->resval.rreal;
  835.                            break;
  836.                         }
  837.                         case 41: {
  838.                            CandidateEndWidth = CandidateEALItem->resval.rreal;
  839.                            break;
  840.                         }
  841.                         case 70: {
  842.                            CandidateFlag = CandidateEALItem->resval.rint;
  843.                            break;
  844.                         }
  845.                         case 75: {
  846.                            CandidateFit = CandidateEALItem->resval.rint;
  847.                            break;
  848.                         }
  849.                         case 210: {
  850.                            ads_point_set(CandidateEALItem->resval.rpoint, CandidateEVec);
  851.                            break;
  852.                         }
  853.                      }
  854.                      CandidateEALItem = CandidateEALItem->rbnext;
  855.                   }
  856.  
  857.                   ads_relrb(CandidateEAL);
  858.  
  859.                   /* Make sure that the candidate's start and end widths are
  860.                      close enough, it's the same type of polyline, the curve
  861.                      fit type is the same, and the extrusion vector is
  862.                      pointing close enough to the same direction */
  863.  
  864.                   if ((fabs(CandidateStartWidth-CurrentEntitySize1) <= Tolerance)
  865.                         && (fabs(CandidateEndWidth-CurrentEntitySize2) <= Tolerance)
  866.                         && (CandidateFlag == CurrentEntityFlag)
  867.                         && (CandidateFit == CurrentEntityFit)
  868.                         && (ads_distance(CurrentEntityEVec, CandidateEVec) <= Tolerance)) {
  869.  
  870.                      /* The candidate may not have as many vertices as
  871.                         the current entity.  We have to first
  872.                         synchronize by sweeping through the vertices of
  873.                         the current entity looking for one that matches
  874.                         the first vertex of the candidate */
  875.  
  876.                      /* Loop over all vertices of the current entity
  877.                         until we run out of vertices or synchronize
  878.                         with the vertices of the candidate.  Start
  879.                         with the first vertex of the candidate, and with
  880.                         the name of the current entity so we'll get
  881.                         the first vertex with the first ads_entnext */
  882.  
  883.                      ads_name_set(CurrentEntityName, CurrentEntityVertex);
  884.                      ads_entnext(CandidateName, CandidateVertex);
  885.                      CandidateVertexData = GetVertexData(CandidateVertex);
  886.                      Synchronized = ReachedLastVertex = FALSE;
  887.  
  888.                      while (!ReachedLastVertex) {
  889.  
  890.                         /* Get the next vertex of the current entity */
  891.  
  892.                         ads_entnext(CurrentEntityVertex, CurrentEntityVertex);
  893.                         CurrentEntityVertexData = GetVertexData(CurrentEntityVertex);
  894.  
  895.                         /* If we've synchronized with the current entity
  896.                            already ... */
  897.  
  898.                         if (Synchronized) {
  899.  
  900.                            /* Get the next vertex of the candidate */
  901.  
  902.                            ads_entnext(CandidateVertex, CandidateVertex);
  903.                            CandidateVertexData = GetVertexData(CandidateVertex);
  904.  
  905.                            /* Have we reached the last vertex of the
  906.                               candidate while still synchronized? */
  907.  
  908.                            if (strcmp(CandidateVertexData.Type, "SEQEND") == 0) {
  909.  
  910.                               /* Yes, we want to delete the candidate */
  911.  
  912.                               ads_entdel(CandidateName);
  913.                               PlinesRemoved = ++PlinesRemoved;
  914.  
  915.                               /* And we're done with this candidate */
  916.  
  917.                               ReachedLastVertex = TRUE;
  918.                            }
  919.                            else {
  920.  
  921.                               /* No, we haven't reached the last vertex of
  922.                                  the candidate.  If the candidate's vertex
  923.                                  data and the current entity's vertex data
  924.                                  are not close enough to equal ... */
  925.  
  926.                               if (!((ads_distance(CurrentEntityVertexData.Location, CandidateVertexData.Location) <= Tolerance)
  927.                                     && (fabs(CurrentEntityVertexData.StartWidth-CandidateVertexData.StartWidth) <= Tolerance)
  928.                                     && (fabs(CurrentEntityVertexData.EndWidth-CandidateVertexData.EndWidth) <= Tolerance)
  929.                                     && (fabs(CurrentEntityVertexData.Bulge-CandidateVertexData.Bulge) <= Tolerance)
  930.                                     && (ads_distance(CurrentEntityVertexData.Tangent, CandidateVertexData.Tangent) <= Tolerance)
  931.                                     && (CurrentEntityVertexData.Flags == CandidateVertexData.Flags))) {
  932.                                        
  933.                                  /* Then we've fallen out of
  934.                                     synchronization. Leave the current
  935.                                     entity's vertex as it is and reset
  936.                                     the candidate's vertex to its first
  937.                                     vertex to attempt resynchronization
  938.                                  */
  939.  
  940.                                  Synchronized = FALSE;
  941.                                  ads_entnext(CandidateName, CandidateVertex);
  942.                                  CandidateVertexData = GetVertexData(CandidateVertex);
  943.                               }
  944.                            }
  945.                         }
  946.                         else {
  947.  
  948.                            /* No, we haven't yet synchronized the two
  949.                               polylines.  Have we reached the last vertex
  950.                               of the current entity? */
  951.  
  952.                            if (strcmp(CurrentEntityVertexData.Type, "SEQEND") == 0) {
  953.  
  954.                               /* Yes, so we can't synchronize these two
  955.                                  entities */
  956.  
  957.                               ReachedLastVertex = TRUE;
  958.                            }
  959.                            else {
  960.  
  961.                               /* No, we haven't run out of vertices on the
  962.                                  current entity; see if we've achieved
  963.                                  synchronization */
  964.  
  965.                               if ((ads_distance(CurrentEntityVertexData.Location, CandidateVertexData.Location) <= Tolerance)
  966.                                     && (fabs(CurrentEntityVertexData.StartWidth-CandidateVertexData.StartWidth) <= Tolerance)
  967.                                     && (fabs(CurrentEntityVertexData.EndWidth-CandidateVertexData.EndWidth) <= Tolerance)
  968.                                     && (fabs(CurrentEntityVertexData.Bulge-CandidateVertexData.Bulge) <= Tolerance)
  969.                                     && (ads_distance(CurrentEntityVertexData.Tangent, CandidateVertexData.Tangent) <= Tolerance)
  970.                                     && (CurrentEntityVertexData.Flags == CandidateVertexData.Flags)) {
  971.  
  972.                                  /* We've synchronized! */
  973.  
  974.                                  Synchronized = TRUE;
  975.                               }
  976.                            }
  977.                         }
  978.                      }
  979.                   }
  980.                }
  981.             }
  982.             ads_ssfree(DeletionCandidates);
  983.          }
  984.             
  985.       }
  986.  
  987.       /* Report if it's time */
  988.  
  989.       if (((CurrentEntityNumber/ReportIncrement)*ReportIncrement) == CurrentEntityNumber) {
  990.          ads_printf("\rExtra Entity Removal %Ld%% done",
  991.             (100*CurrentEntityNumber)/NumEntsToCheck);
  992.       }
  993.    }
  994.  
  995.    /* End our undo group */
  996.  
  997.    ads_command(RTSTR, "._UNDO", RTSTR, "_END", RTNONE);
  998.  
  999.    EndTime = clock();
  1000.    
  1001.    ads_printf("\rRemoved %Ld line%s, ", LinesRemoved, (LinesRemoved-1 ? "s" : ""));
  1002.    ads_printf("%Ld circle%s, ", CirclesRemoved, (CirclesRemoved-1 ? "s" : ""));
  1003.    ads_printf("%Ld arc%s, ", ArcsRemoved, (ArcsRemoved-1 ? "s" : ""));
  1004.    ads_printf("%Ld polyline%s, ", PlinesRemoved, (PlinesRemoved-1 ? "s" : ""));
  1005.    ads_printf("and %Ld point%s;", PointsRemoved, (PointsRemoved-1 ? "s" : ""));
  1006.  
  1007.    TotalRemoved = LinesRemoved + CirclesRemoved + ArcsRemoved + PlinesRemoved + PointsRemoved;
  1008.  
  1009.    ads_printf("\nTotal %Ld entit%s", TotalRemoved, (TotalRemoved-1 ? "ies" : "y"));
  1010.    ads_printf(" (%Ld%%) removed in ", (100*TotalRemoved)/NumEntsToCheck);
  1011.  
  1012.    /* Get the total number of seconds used, and convert it to hours minutes and
  1013.       seconds */
  1014.  
  1015.    TotalSeconds = ((float) (EndTime - StartTime))/((float) CLOCKS_PER_SEC);
  1016.    SecToHMS(TotalSeconds, &ElapsedHours, &ElapsedMinutes, &ElapsedSeconds);
  1017.  
  1018.    if (ElapsedHours != 0L) {
  1019.       ads_printf("%Ld hour%s ", ElapsedHours, ElapsedHours-1 ? "s" : "");
  1020.    }
  1021.  
  1022.    if (ElapsedMinutes != 0L) {
  1023.       ads_printf("%Ld minute%s ", ElapsedMinutes, ElapsedMinutes-1 ? "s" : "");
  1024.    }
  1025.  
  1026.    ads_printf("%.1f second%s", ElapsedSeconds, ElapsedSeconds-1 ? "s" : "");
  1027.  
  1028.    /* If we took long enough to calculate a meaningful processing rate,
  1029.       do so. */
  1030.  
  1031.    if (TotalSeconds >= 2.0) {
  1032.  
  1033.       /* Figure out the entities processed per second, and figure
  1034.          out how many places to show after the decimal point to
  1035.          have at least (and usually) two significant figures */
  1036.  
  1037.       EntitiesPerSecond = ((float) NumEntsToCheck)/TotalSeconds;
  1038.       if ((TempReal = log10(EntitiesPerSecond)) > 0) {
  1039.          if (TempReal >= 1) {
  1040.             DecimalPlaces = 0;
  1041.          }
  1042.          else {
  1043.             DecimalPlaces = 1;
  1044.          }
  1045.       }
  1046.       else {
  1047.          DecimalPlaces = 1 + ceil(-TempReal);
  1048.       }
  1049.  
  1050.       /* Print out the result */
  1051.  
  1052.       if (DecimalPlaces < 8) {
  1053.          ads_printf(" (%.*f entities processed/second)", DecimalPlaces, EntitiesPerSecond);
  1054.       }
  1055.       else {
  1056.          ads_printf(" (%.1E entities processed/second)", DecimalPlaces, EntitiesPerSecond);
  1057.       }
  1058.    }
  1059.    ads_printf("\n");
  1060.  
  1061.    /* Redraw the screen so the user sees what's really there */
  1062.  
  1063.    ads_redraw(NULL, NULL);
  1064. }
  1065.